<!-- 表单 》 文件 -->

<template>
  <el-form-item v-bind="formItem">
    <!-- 标签 -->
    <template v-if="hasLabelSlot" #label>
      <slot name="label" :label="formItem.label">{{ formItem.label }}</slot>
    </template>

    <draggable v-if="model.length" v-model="model" v-bind="draggableProps" @end="draged">
      <!-- 预览文件列表 (拖拽项) -->
      <div v-for="(item, i) in dataList" :key="item.id">
        <!-- 文件 -->
        <div :class="`${className}-name`">
          <img v-if="ICON.preImg" :src="ICON.preImg" />
          <i v-else-if="ICON.preIcon" :class="ICON.preIcon" />
          <span>{{ item.name }}</span>
        </div>

        <!-- 删除 -->
        <div :class="delClass" @click="toDel(i)">
          <img v-if="ICON.delImg" :src="ICON.delImg" />
          <i v-else :class="ICON.delIcon" />
        </div>
      </div>
    </draggable>

    <!-- 上传区域 -->
    <div :class="uploadClass" :style="uploadStyle">
      <el-upload v-bind="uploadProps">
        <div>
          <img v-if="ICON.upImg" :src="ICON.upImg" />
          <i v-else :class="ICON.upIcon" />

          <div v-if="text">{{ text }}</div>
        </div>
      </el-upload>
    </div>
  </el-form-item>
</template>

<script>
// ============================== 导入依赖 ============================== //

import CORE from '@/CORE'
import mixForm from '../mixForm'
import draggable from 'vuedraggable'
import UTIL from '../../row/RowOperate/UTIL'

const name = 'FormFile'

// ============================== 导出组件 ============================== //

export default {
  /**
   * 名称定义 (实际使用名称)
   */
  name,

  /**
   * 混入列表 (配置选项合并)
   */
  mixins: [mixForm],

  /**
   * 组件导入 (Abc.vue + Abc/index.vue)
   */
  ...CORE.components(draggable),

  /**
   * 属性注册 (抽取以便查阅)
   */
  props: CORE.WEB[name],

  /**
   * 状态数据 (函数返回对象)
   */
  data() {
    return {
      model: [], // 内部绑定值
      EMPTY: [], // 空值常量
    }
  },

  /**
   * 计算属性 (慎用箭头函数)
   */
  computed: {
    /**
     * 上传类型
     */
    uploadType() {
      return this.$options.name.slice(4).toLowerCase()
    },

    /**
     * 业务标识
     */
    businessTag() {
      return this.isPublic ? 'public' : 'service'
    },

    /**
     * 图标对象
     */
    ICON() {
      const { img, icon, preImg, preIcon, delImg, delIcon } = this
      return {
        upImg: UTIL.getImg(img),
        upIcon: UTIL.getImg(icon) || 'el-icon-upload',
        preImg: UTIL.getImg(preImg),
        preIcon: UTIL.getImg(preIcon) || 'el-icon-document',
        delImg: UTIL.getImg(delImg),
        delIcon: UTIL.getImg(delIcon) || 'el-icon-close',
      }
    },

    /**
     * 数据列表
     */
    dataList() {
      const { model, pathKey, pathTrans, noTrans } = this
      const transId = (id, path, i) => id || `${i}-${path}`
      const transName = (path) => path.split('/').pop().replace(/\?.*$/, '')

      if (!pathKey) {
        return model.map((path, i) => ({
          id: transId('', path, i),
          path: noTrans ? path : pathTrans(path),
          name: transName(path),
        }))
      } else {
        return model.map(({ id, name, path, ...other }, i) => ({
          id: transId(id, path, i),
          name: name || transName(path),
          path: noTrans ? path : pathTrans(path),
          ...other,
        }))
      }
    },

    /**
     * 删除的样式类
     */
    delClass() {
      const { className, commonProps, noDel, allDel } = this
      if (noDel || commonProps.disabled) return `${className}-del ${className}-del-hide`
      if (allDel) return `${className}-del ${className}-del-show`
      return `${className}-del`
    },

    /**
     * 拖拽属性
     */
    draggableProps() {
      const { dragProps, animation, commonProps } = this
      return {
        ...dragProps,
        animation,
        disabled: commonProps.disabled,
        class: commonProps.class,
      }
    },

    /**
     * 上传属性
     */
    uploadProps() {
      const { elProps, accept, beforeUpload } = this
      return {
        ...elProps,
        action: '',
        drag: true,
        showFileList: false,
        accept,
        beforeUpload,
      }
    },

    /**
     * 上传样式类
     */
    uploadClass() {
      const { className, commonProps, model, limit } = this
      const needHide = commonProps.disabled || (limit && model.length >= limit)
      if (needHide) return `${className}-upload ${className}-upload-hide`
      return `${className}-upload`
    },

    /**
     * 上传样式对象
     */
    uploadStyle() {
      const { getSize, square } = this
      const [w, h] = CORE.isArray(square) ? square : [square, square]
      const width = getSize(w)
      const height = getSize(h)
      return { width, height }
    },
  },

  /**
   * 本地方法 (轻量重要在前)
   */
  methods: {
    /**
     * 是否不等 (可覆盖)
     */
    isDiff() {
      const { obj, prop, model, join, pathKey, nameKey, typeKey, sizeKey, idKey } = this
      const value = obj[prop]

      // 无值 (如 undefined / null / '' / [] 等)
      if (CORE.isEmpty(value)) return !CORE.isEmpty(model)

      // 合并串
      if (join) return value !== model.join()
      // 路径列表
      if (!pathKey) return value.join() !== model.join()
      // 对象列表，长度不等
      if (value.length !== model.length) return true
      // 对象列表，各字段比较
      return value.some((item, i) => {
        if (!CORE.isJSON(item) || !CORE.isJSON(model[i])) return true
        if (nameKey && item[nameKey] !== model[i].name) return true
        if (typeKey && item[typeKey] !== model[i].type) return true
        if (sizeKey && item[sizeKey] !== model[i].size) return true
        if (idKey && item[idKey] !== model[i].id) return true
        return item[pathKey] !== model[i].path
      })
    },

    /**
     * 设置内部绑定值 (可覆盖)
     */
    setModel() {
      const { obj, prop, init, EMPTY, join, pathKey, nameKey, typeKey, sizeKey, idKey } = this
      const value = obj[prop]

      // 无值 (如 undefined / null / '' / [] 等)
      if (CORE.isEmpty(value)) this.model = init || EMPTY
      // 合并串
      else if (join) this.model = value.split(',')
      // 路径列表
      else if (!pathKey) this.model = value
      // 对象列表
      else {
        this.model = value.map((item) => ({
          id: item[idKey] || '',
          path: item[pathKey] || '',
          name: item[nameKey] || '',
          type: item[typeKey] || 0,
          size: item[sizeKey] || 0,
        }))
      }
    },

    /**
     * 获取最终回传值 (可覆盖)
     */
    getValue() {
      const { obj, prop, model, join, pathKey, nameKey, typeKey, sizeKey, fileObj, idKey } = this
      // 合并串
      if (join) return model.join()

      // 路径列表
      if (!pathKey) return model

      // 对象列表
      const values = obj[prop] || []
      return model.map((item) => {
        // 旧对象
        const matched = values.find((value) => value[idKey] === item.id)
        if (matched) return matched

        // 新上传
        const result = { ...CORE.clone(fileObj || {}), [pathKey]: item.path }
        if (nameKey) result[nameKey] = item.name
        if (typeKey) result[typeKey] = item.type
        if (sizeKey) result[sizeKey] = item.size
        if (idKey) result[idKey] = item.id
        return result
      })
    },

    /**
     * 是否相等 (可覆盖)
     */
    equals(value) {
      const { obj, prop } = this
      return JSON.stringify(obj[prop] || '') === JSON.stringify(value || '')
    },

    /**
     * 检查文件
     */
    checkFile(file) {
      const { max, maxErrPrefix, maxErrSuffix, accept } = this
      let msgError

      if (max && file.size > max * 1048576) msgError = `${maxErrPrefix} ${max} ${maxErrSuffix}`
      else if (/^image/.test(accept) && !CORE.MAP.REG_IMAGE.test(file.name)) msgError = '请上传图片格式的文件'
      else if (/^audio/.test(accept) && !CORE.MAP.REG_AUDIO.test(file.name)) msgError = '请上传音频格式的文件'
      else if (/^video/.test(accept) && !CORE.MAP.REG_VIDEO.test(file.name)) msgError = '请上传视频格式的文件'

      msgError && CORE.MSG.error(msgError)

      return !msgError
    },

    /**
     * 获取文件夹
     */
    getFolder() {
      const { isPublic, folder } = this
      if (isPublic) return folder

      const today = CORE.today()
      return `${folder}/${today.slice(0, 4)}/${+today.slice(5, 7)}/${+today.slice(8)}`
    },

    /**
     * 上传之前
     */
    beforeUpload(file) {
      if (!this.checkFile(file)) return false

      const { uid, name, size, type: fileType } = file

      const folder = this.getFolder()
      const tag = this.businessTag
      const type = this.uploadType

      const success = (path) => {
        const item = this.pathKey ? { path, name, size, id: `${uid}`, type: this.typeTrans(fileType) } : path
        this.model.push(item)
        this.emitChange()
      }
      const error = (err) => CORE.MSG.error(err.message || '上传失败')

      if (this.upload) this.upload(file, folder, tag, type).then(success).catch(error)
      else CORE[`${type}Upload`](file, folder, tag).then(success).catch(error)

      return false
    },

    /**
     * 拖拽完成
     */
    draged() {
      const { emitChange } = this
      emitChange()
    },

    /**
     * 点击删除
     */
    toDel(i) {
      const { emitChange } = this
      this.model.splice(i, 1)
      emitChange()
    },
  },
}
</script>
